/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable no-param-reassign */
/* eslint-disable no-unused-expressions */
/* eslint-disable no-case-declarations */
import { ISearchBar, ISearchBarFilter } from '@ibiz/model-core';
import {
  ISearchCond,
  ItemsValueOPs,
  ScriptFactory,
  isSimpleItems,
  convertNavData,
  ISearchBarEvent,
  getEntitySchema,
  ISearchCondGroup,
  ControlController,
  MDControlController,
  SearchBarFilterController,
  SearchBarFilterItemsController,
  SearchBarFilterSimpleItemsController,
} from '@ibiz-template/runtime';
import { isString } from 'lodash-es';
import { clone, recursiveIterate } from '@ibiz-template/core';
import {
  IGroupItem,
  IFilterItem,
  ISearchItem,
  ICollectItem,
  SearchItemType,
  ScriptValueRegex,
  IOdooSearchBarState,
  IOdooSearchBarController,
} from './interfaces';
import { OdooSearchBarService } from './odoo-search-bar.service';
import { calcFilterModelBySchema } from './entity-schema';

/**
 * 搜索栏控制器
 * @export
 * @class OdooSearchBarController
 * @extends {ControlController}
 */
export class OdooSearchBarController
  extends ControlController<ISearchBar, IOdooSearchBarState, ISearchBarEvent>
  implements IOdooSearchBarController
{
  /**
   * @description 快速搜索占位符（根据属性计算出来的快速搜索占位符）
   * @memberof OdooSearchBarController
   */
  placeHolder = '';

  /**
   * @description 搜索栏服务
   * @type {OdooSearchBarService}
   * @memberof OdooSearchBarController
   */
  service!: OdooSearchBarService;

  /**
   * @description 过滤项控制器
   * @type {SearchBarFilterController[]}
   * @memberof OdooSearchBarController
   */
  filterControllers: SearchBarFilterController[] = [];

  /**
   * @description schema实体映射map
   * @type {(Map<string, string | undefined>)}
   * @memberof OdooSearchBarController
   */
  schemaEntityMap: Map<string, string | undefined> = new Map();

  /**
   * @description 互斥过滤项
   * @type {{ [key: string]: string[] }}
   * @memberof OdooSearchBarController
   */
  exclusive: { [key: string]: string[] } = {};

  /**
   * @description OR关系组
   * @type {{ [key: string]: string[] }}
   * @memberof OdooSearchBarController
   */
  filterORgroup: { [key: string]: string[] } = {};

  /**
   * @description 是否有默认选中
   * @memberof OdooSearchBarController
   */
  hasDefaultSelect = false;

  /**
   * 数据部件控制器（多数据）
   * @readonly
   * @type {MDControlController}
   */
  get xdataControl(): MDControlController | undefined {
    const { xdatacontrolname = '' } = this.view.state as IData;
    if (xdatacontrolname) {
      return this.view.getController(xdatacontrolname) as MDControlController;
    }
  }

  /**
   * @description 时间格式化
   * @memberof OdooSearchBarController
   */
  public dateFormat = [
    {
      label: '年',
      value: 'year',
    },
    {
      label: '季',
      value: 'quarter',
    },
    {
      label: '月',
      value: 'month',
    },
    {
      label: '周',
      value: 'week',
    },
    {
      label: '天',
      value: 'day',
    },
  ];

  /**
   * @description 初始化UI状态
   * @protected
   * @memberof OdooSearchBarController
   */
  protected initState(): void {
    super.initState();
    const { groupMode } = this.model;
    this.state.singleSelect = !groupMode || groupMode === 'SINGLE';
    this.state.condop = groupMode === 'OR' ? 'OR' : 'AND';
    this.state.filter = [];
    this.state.collect = [];
    this.state.group = [];
    this.state.selectedFilter = [];
    this.state.schemaFields = [];
    this.state.selectedCollect = undefined;
    this.state.selectedGroup = [];
    this.state.collectName = this.view.model.caption!;
    this.state.showFilter = true;
    this.state.showGroup = true;
    this.state.showCollect = true;
  }

  /**
   * @description 生命周期-创建完成
   * @protected
   * @returns {*}  {Promise<void>}
   * @memberof OdooSearchBarController
   */
  protected async onCreated(): Promise<void> {
    this.service = new OdooSearchBarService(this.model, this.context);
    await super.onCreated();
    await this.initFiltersByJsonSchema();
    await this.initSearchBarFilters();
    await this.initSearchBar();
    this.initFilterByUserParam();
  }

  /**
   * @description 通过用户参数初始化过滤配置
   * @private
   * @memberof OdooSearchBarController
   */
  private initFilterByUserParam(): void {
    const { searchBarGroups } = this.model;
    if (!searchBarGroups || !searchBarGroups.length) return;
    searchBarGroups.forEach(group => {
      // 互斥项
      if (group.userParam?.exclusive) {
        const exclusive = this.exclusive[group.userParam.exclusive] || [];
        exclusive.push(group.id!);
        this.exclusive[group.userParam.exclusive] = exclusive;
      }
      // or条件组
      if (group.userParam?.filterorgroup) {
        const filterorgroup =
          this.filterORgroup[group.userParam.filterorgroup] || [];
        filterorgroup.push(group.id!);
        this.filterORgroup[group.userParam.filterorgroup] = filterorgroup;
      }
    });
  }

  /**
   * @description 初始化schema实体映射map
   * @private
   * @param {IData} json
   * @returns {*}  {void}
   * @memberof OdooSearchBarController
   */
  private initSchemaEntityMap(json: IData): void {
    if (!json.properties) return;
    const { properties } = json;
    if (!(Object.keys(properties).length > 0)) return;
    const map = new Map();
    Object.keys(properties).forEach((key: string) => {
      map.set(key, properties[key]?.$ref?.split('.')[0]);
    });
    this.schemaEntityMap = map;
  }

  /**
   * @description 通过JsonSchema初始化过滤项模型
   * @returns {*}  {Promise<void>}
   * @memberof OdooSearchBarController
   */
  async initFiltersByJsonSchema(): Promise<void> {
    const params = convertNavData(
      JSON.parse(this.controlParams.jsonschemaparams || '{}'),
      this.params,
      this.context,
    );
    Object.assign(params, this.params);
    const json = await getEntitySchema(
      this.model.appDataEntityId!,
      this.context,
      params,
    );
    if (!json) return;
    this.initSchemaEntityMap(json);
    const { schemaFields, searchBarFilters } = await calcFilterModelBySchema(
      json,
      this.model.appDataEntityId!,
      this.model.appId,
    );
    this.state.schemaFields = schemaFields;
    const mergeFilters: ISearchBarFilter[] = [];
    this.model.searchBarFilters?.forEach(filter => {
      const findindex = searchBarFilters.findIndex(
        item =>
          item.appDEFieldId === filter.appDEFieldId &&
          (filter.defsearchMode?.valueOP
            ? item.defsearchMode!.valueOP === filter.defsearchMode.valueOP
            : true),
      );
      if (findindex === -1) {
        mergeFilters.push(filter);
      }
    });
    // 如果有根据json计算出的过滤项，则要重置相关state参数
    if (searchBarFilters.length) {
      // 修改模型之前拷贝一份，避免污染原始数据
      (this as IData).model = clone(this.model);
      this.model.searchBarFilters = mergeFilters.concat(...searchBarFilters);
    }
  }

  /**
   * @description 初始化过滤项控制器
   * @returns {*}  {Promise<void>}
   * @memberof OdooSearchBarController
   */
  async initSearchBarFilters(): Promise<void> {
    const { searchBarFilters, appDataEntityId } = this.model;
    if (!searchBarFilters || !searchBarFilters.length) return;
    const appDataEntity = await ibiz.hub.getAppDataEntity(
      appDataEntityId!,
      this.context.srfappid,
    )!;
    const itemsMap = new Map<string, ISearchBarFilter[]>();
    searchBarFilters.forEach(item => {
      // 整理exists或者NOTEXISTS的模型
      if (
        item.defsearchMode?.valueOP &&
        ItemsValueOPs.includes(item.defsearchMode.valueOP)
      ) {
        if (isSimpleItems(item)) {
          this.filterControllers.push(
            new SearchBarFilterSimpleItemsController(
              item,
              appDataEntity,
              this.context,
              this.params,
            ),
          );
          return;
        }

        const key = `${item.appDEFieldId}_${item.defsearchMode.valueOP}`;
        if (!itemsMap.has(key)) {
          itemsMap.set(key, []);
        }
        itemsMap.get(key)!.push(item);
        return;
      }

      // 常规的属性过滤项
      const filterController = new SearchBarFilterController(
        item,
        appDataEntity,
        this.context,
        this.params,
      );
      this.filterControllers.push(filterController);
    });

    // 初始化SearchBarFilterItemsController
    if (itemsMap.size > 0) {
      itemsMap.forEach(items => {
        const filterController = new SearchBarFilterItemsController(
          items,
          appDataEntity,
          this.context,
          this.params,
        );
        this.filterControllers.push(filterController);
      });
    }

    await Promise.all(
      this.filterControllers.map(controller => controller.init()),
    );
  }

  /**
   * @description 初始化搜索栏
   * @returns {*}  {Promise<void>}
   * @memberof OdooSearchBarController
   */
  async initSearchBar(): Promise<void> {
    const { searchBarGroups, controlParam } = this.model;
    if (searchBarGroups && searchBarGroups.length) {
      this.state.filter = searchBarGroups.map(item => {
        const temp: IFilterItem = {
          id: item.id!,
          isShow: true,
          searchconds: [],
          isCustom: false,
          isChange: false,
          caption: item.caption!,
          showSeparator: item.addSeparator === true,
        };
        if (item.data) {
          try {
            // 解析data属性到对应位置
            const data = JSON.parse(item.data);
            if (data.theme_model?.searchconds)
              temp.searchconds = data.theme_model.searchconds;
            temp.isShow = data.valid_flag === '1';
          } catch (error) {
            ibiz.log.error(
              ibiz.i18n.t('runtime.controller.control.searchBar.JSONFormat', {
                data: item.data,
              }),
              error,
            );
          }
        }
        return temp;
      });
    }
    const defaultGroup = controlParam?.ctrlParams?.DEFAULTGROUP;
    if (defaultGroup) {
      const keys: string[] = defaultGroup.split(',');
      keys.forEach(key => {
        const field = this.state.schemaFields.find(
          item => key.toLowerCase() === item.key.toLowerCase(),
        );
        if (field)
          this.state.group.push({
            id: field.key!,
            isCustom: false,
            type: field.type,
            appDEFieldId: field.key,
            caption: field.description,
            appCodeListId: field.enumSource,
          });
      });
    }
    const data = await this.service.load();
    if (data) {
      const { filter, collect, group } = data;
      if (filter?.length) {
        filter.forEach((item: IFilterItem) => {
          const index = this.state.filter.findIndex(f => f.id === item.id);
          index === -1
            ? this.state.filter.push({ ...item, isCustom: true })
            : this.state.filter.splice(index, 1, item);
        });
      }
      if (collect?.length) this.state.collect = collect;
      if (group?.length) this.state.group.push(...group);
    }
    const defaultFilter = searchBarGroups?.find(
      searchBar => searchBar.defaultGroup,
    );
    if (defaultFilter) {
      this.hasDefaultSelect = true;
      const filter = this.state.filter.find(f => f.id === defaultFilter.id)!;
      this.handleFilterClick(filter);
    }
  }

  /**
   * @description 根据标识获取指定预置项的搜索分组数据
   * @param {string} id
   * @returns {*}  {ISearchCondGroup[]}
   * @memberof OdooSearchBarController
   */
  getSearchCondsById(id: string):
    | {
        searchconds: ISearchCondGroup[];
        caption: string;
      }
    | undefined {
    let searchconds: ISearchCondGroup[] = [];
    const { searchBarGroups } = this.model;
    const item = searchBarGroups?.find(s => s.id === id);
    if (!item) return;
    if (item.data) {
      try {
        // 解析data属性到对应位置
        const data = JSON.parse(item.data);
        if (data.theme_model?.searchconds)
          searchconds = data.theme_model.searchconds;
      } catch (error) {
        ibiz.log.error(
          ibiz.i18n.t('runtime.controller.control.searchBar.JSONFormat', {
            data: item.data,
          }),
          error,
        );
      }
    }
    return {
      searchconds,
      caption: item.caption!,
    };
  }

  /**
   * 根据类型清空选中数据
   *
   * @param {SearchItemType} item
   * @return {*}  {Promise<void>}
   * @memberof OdooSearchBarController
   */
  async onClearSelectedByType(item: ISearchItem): Promise<void> {
    switch (item.type) {
      case 'filter':
        if (item.filterType === 'OR' && item.filterTypeId) {
          this.state.selectedFilter = this.state.selectedFilter.filter(
            selected => {
              const orgroup = this.filterORgroup[item.filterTypeId!];
              if (orgroup.includes(selected.id)) return false;
              return true;
            },
          );
        } else if (item.filterType === 'EXCLUSIVE' && item.filterTypeId) {
          this.state.selectedFilter = this.state.selectedFilter.filter(
            selected => {
              const exclusive = this.exclusive[item.filterTypeId!];
              if (exclusive.includes(selected.id)) return false;
              return true;
            },
          );
        } else {
          this.state.selectedFilter = this.state.selectedFilter.filter(
            selected => {
              const isInORGroup = Object.keys(this.filterORgroup).some(orKey =>
                this.filterORgroup[orKey].includes(selected.id),
              );
              const isExclusive = Object.values(this.exclusive).some(exGroup =>
                exGroup.includes(selected.id),
              );
              if (isInORGroup || isExclusive) return true;
              return false;
            },
          );
        }
        await this.onSearch();
        break;
      case 'collect':
        this.state.selectedCollect = undefined;
        await this.onSearch();
        break;
      case 'group':
        this.state.selectedGroup.forEach(group => {
          group.dateFormat = [];
          group.isExpand = false;
        });
        this.state.selectedGroup = [];
        this.execGroup();
        break;
      default:
        break;
    }
  }

  /**
   * @description 根据类型更新数据
   * @param {SearchItemType} type
   * @param {('update' | 'remove')} actionName
   * @param {(IFilterItem | IGroupItem | ICollectItem)} item
   * @returns {*}  {Promise<void>}
   * @memberof OdooSearchBarController
   */
  async updateByType(
    type: SearchItemType,
    actionName: 'update' | 'remove',
    item: IFilterItem | IGroupItem | ICollectItem,
  ): Promise<void> {
    switch (type) {
      case 'filter':
        await this.handleFilterUpdate(actionName, item as IFilterItem);
        break;
      case 'collect':
        await this.handleCollectUpdate(actionName, item as ICollectItem);
        break;
      case 'group':
        await this.handleGroupUpdate(actionName, item as IGroupItem);
        break;
      default:
        break;
    }
  }

  /**
   * @description 处理过滤值更新
   * @private
   * @param {('update' | 'remove')} actionName
   * @param {IFilterItem} item
   * @returns {*}  {Promise<void>}
   * @memberof OdooSearchBarController
   */
  private async handleFilterUpdate(
    actionName: 'update' | 'remove',
    item: IFilterItem,
  ): Promise<void> {
    const { selectedFilter, filter } = this.state;
    let index = filter.findIndex(f => f.id === item.id);
    if (actionName === 'update') {
      index !== -1 ? filter.splice(index, 1, item) : filter.push(item);
    } else if (index !== -1) {
      filter.splice(index, 1);
      // 如果删除选中项则同步删除选中数据
      index = selectedFilter.findIndex(f => f.id === item.id);
      if (index !== -1) await this.handleFilterClick(item);
    }
    await this.save();
  }

  /**
   * @description 处理收藏更新
   * @private
   * @param {('update' | 'remove')} actionName
   * @param {ICollectItem} item
   * @returns {*}  {Promise<void>}
   * @memberof OdooSearchBarController
   */
  private async handleCollectUpdate(
    actionName: 'update' | 'remove',
    item: ICollectItem,
  ): Promise<void> {
    // 收藏只有新建和删除
    const { collect, selectedCollect } = this.state;
    const index = collect.findIndex(c => c.id === item.id);
    if (actionName === 'update' && index === -1) {
      // 新建收藏时默认选中
      collect.push(item);
      this.state.selectedFilter = [];
      this.state.selectedGroup = [];
      await this.handleCollect(item);
    } else if (index !== -1) {
      collect.splice(index, 1);
      // 如果删除选中项则同步删除选中数据
      if (selectedCollect?.id === item.id) await this.handleCollect(item);
    }
    await this.save();
  }

  /**
   * @description 处理分组更新
   * @private
   * @param {('update' | 'remove')} actionName
   * @param {IGroupItem} item
   * @returns {*}  {Promise<void>}
   * @memberof OdooSearchBarController
   */
  private async handleGroupUpdate(
    actionName: 'update' | 'remove',
    item: IGroupItem,
  ): Promise<void> {
    // 分组只有新建和删除
    const { group, selectedGroup } = this.state;
    let index = group.findIndex(c => c.id === item.id);
    if (actionName === 'update' && index === -1) {
      group.push(item);
      if (item.type !== 'date') this.handleGroupClick(item);
    } else if (index !== -1) {
      group.splice(index, 1);
      index = selectedGroup.findIndex(s => s.id === item.id);
      // 如果删除选中项则同步删除选中数据
      if (index !== -1) this.handleGroupClick(item);
    }
    await this.save();
  }

  /**
   * @description 搜索项点击
   * @param {SearchItemType} type 数据类型
   * @param {(IFilterItem | IGroupItem | ICollectItem)} item 数据项
   * @returns {*}  {Promise<void>}
   * @memberof OdooSearchBarController
   */
  async onSearchItemClick(
    type: SearchItemType,
    item: IFilterItem | IGroupItem | ICollectItem,
  ): Promise<void> {
    switch (type) {
      case 'filter':
        this.handleFilterClick(item as IFilterItem);
        break;
      case 'collect':
        this.handleCollect(item as ICollectItem);
        break;
      case 'group':
        this.handleGroupClick(item as IGroupItem);
        break;
      default:
        break;
    }
  }

  /**
   * @description 处理过滤点击
   * @private
   * @param {IFilterItem} item
   * @returns {*}  {Promise<void>}
   * @memberof OdooSearchBarController
   */
  private async handleFilterClick(item: IFilterItem): Promise<void> {
    const { singleSelect } = this.state;
    const index = this.state.selectedFilter.findIndex(
      selected => selected.id === item.id,
    );
    if (singleSelect) {
      this.state.selectedFilter = index !== -1 ? [] : [item];
    } else {
      index !== -1
        ? this.state.selectedFilter.splice(index, 1)
        : this.state.selectedFilter.push(item);
      // 处理互斥项
      const exclusive = Object.values(this.exclusive).find(ex =>
        ex.includes(item.id),
      );
      if (exclusive?.length)
        this.state.selectedFilter = this.state.selectedFilter.filter(s => {
          if (exclusive.includes(s.id)) return item.id === s.id;
          return true;
        });
    }
    await this.onSearch();
  }

  /**
   * @description 处理分组点击
   * @private
   * @param {IGroupItem} item
   * @memberof OdooSearchBarController
   */
  private handleGroupClick(item: IGroupItem): void {
    const { selectedGroup } = this.state;
    const index = selectedGroup.findIndex(selected => selected.id === item.id);
    index !== -1 ? selectedGroup.splice(index, 1) : selectedGroup.push(item);
    this.execGroup();
  }

  /**
   * @description 执行分组
   * @memberof OdooSearchBarController
   */
  execGroup(): void {
    if (!this.xdataControl) return;
    const { selectedGroup, selectedCollect } = this.state;
    const groups = clone(selectedGroup);
    if (selectedCollect?.groups?.length) {
      selectedCollect.groups.forEach(group => {
        const index = selectedGroup.findIndex(s => s.id === group.id);
        index !== -1 ? groups.splice(index, 1, group) : groups.push(group);
      });
    }
    const groupParams: any[] = groups.map(item => {
      return {
        groupFieldId: item.appDEFieldId,
        groupCodeListId: item.appCodeListId,
        dateFormat: this.dateFormat
          .filter(d => item.dateFormat?.includes(d.value))
          .map(d => d.value),
      };
    });
    (this.xdataControl as any).execGroup?.(groupParams);
  }

  /**
   * @description 处理收藏点击
   * @private
   * @param {ICollectItem} item
   * @returns {*}  {Promise<void>}
   * @memberof OdooSearchBarController
   */
  private async handleCollect(item: ICollectItem): Promise<void> {
    const { selectedCollect } = this.state;
    if (selectedCollect?.id !== item.id) {
      this.state.selectedCollect = item;
      const { filterIds, groups } = item;
      if (filterIds.length) await this.onSearch();
      if (groups.length) this.execGroup();
    } else {
      this.state.selectedCollect = undefined;
      await this.onSearch();
      this.execGroup();
    }
  }

  /**
   * @description 计算过滤项参数
   * @private
   * @returns {*}  {(ISearchCondGroup[] | undefined)}
   * @memberof OdooSearchBarController
   */
  private calcFilters(): ISearchCondGroup[] | undefined {
    const { selectedFilter, selectedCollect, filter } = this.state;
    const searchFilter: IFilterItem[] = selectedFilter.map(s => {
      return { ...clone(s) };
    });
    if (selectedCollect)
      filter
        .filter(f => selectedCollect.filterIds.includes(f.id))
        .forEach(s => searchFilter.push({ ...clone(s) }));

    const searchconds: ISearchCondGroup[] = [];

    // 计算 OR 分组
    const { grouped, ungrouped } = searchFilter.reduce<{
      grouped: { [key: string]: ISearchCondGroup };
      ungrouped: ISearchCondGroup[];
    }>(
      (acc, item) => {
        // 查找OR分组
        const OR = Object.keys(this.filterORgroup).find(or =>
          this.filterORgroup[or].includes(item.id),
        );
        if (OR) {
          acc.grouped[OR] = acc.grouped[OR] || {
            condtype: 'GROUP',
            condop: 'OR',
            searchconds: [],
          };
          acc.grouped[OR].searchconds?.push(...item.searchconds);
        } else {
          acc.ungrouped.push(...item.searchconds);
        }
        return acc;
      },
      { grouped: {}, ungrouped: [] },
    );

    searchconds.push(...ungrouped);

    if (Object.keys(grouped).length)
      searchconds.push(...Object.values(grouped));

    recursiveIterate(
      { searchconds },
      (item: ISearchCond) => {
        if (
          item.condtype === 'DEFIELD' &&
          isString(item.value) &&
          ScriptValueRegex.test(item.value)
        ) {
          item.value = ScriptFactory.execSingleLine(`\`${item.value}\``, {
            ...this.getEventArgs(),
          });
        }
      },
      {
        childrenFields: ['searchconds'],
      },
    );
    return searchconds.length
      ? [
          {
            condtype: 'GROUP',
            condop: this.state.condop,
            searchconds,
          },
        ]
      : undefined;
  }

  /**
   * @description 获取搜索栏的过滤参数（包括快速搜索，快速分组和过滤器的参数）
   * @returns {*}  {IParams}
   * @memberof OdooSearchBarController
   */
  getFilterParams(): IParams {
    const params: IParams = {};
    if (this.state.query) params.query = this.state.query;
    const filters = this.calcFilters();
    if (filters) params.searchconds = filters;
    return params;
  }

  /**
   * @description 保存
   * @private
   * @returns {*}  {Promise<void>}
   * @memberof OdooSearchBarController
   */
  private async save(): Promise<void> {
    const { filter, collect, group } = this.state;
    // 分组只保存自定义分组
    const customGroup = group
      .filter(item => item.isCustom)
      .map(item => {
        return {
          id: item.id,
          type: item.type,
          caption: item.caption,
          isCustom: item.isCustom,
          appDEFieldId: item.appDEFieldId,
          appCodeListId: item.appCodeListId,
        };
      });
    // 过滤项只保存变更过的预置过滤项和自定义的过滤项
    const changeFilter = filter.filter(item => item.isCustom || item.isChange);
    await this.service.save({
      collect,
      filter: changeFilter,
      group: customGroup,
    });
  }

  /**
   * @description 搜索
   * @returns {*}  {Promise<void>}
   * @memberof OdooSearchBarController
   */
  async onSearch(): Promise<void> {
    if (this.xdataControl) {
      this.xdataControl.state.searchParams = this.getFilterParams();
      await this.xdataControl.load({ isInitialLoad: true });
    }
  }

  /**
   * @description 重置
   * @returns {*}  {Promise<void>}
   * @memberof OdooSearchBarController
   */
  async resetFilter(): Promise<void> {
    this.state.selectedCollect = undefined;
    this.state.selectedFilter = [];
    this.state.selectedGroup = [];
    await this.onSearch();
  }
}
